// $Id: string.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2008-2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_STRING_HPP
#define XPCOM_STRING_HPP

#include <xpcom/config.hpp>
#include <cstddef>
#include <iterator>
#ifndef XPCOM_NO_IOSTREAM
#include <iosfwd>
#include <string>
#endif // XPCOM_NO_IOSTREAM
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <xpcom/types.hpp>
#include <xpcom/assert.hpp>
#include <xpcom/char_traits.hpp>
#include <xpcom/exception.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {

//
// XPCOM string class.
//
// In most cases, this class's interface equals to std::basic_string's.
// The differences are:
//  1. Can only instantiate by char and xpcom::wchar
//  2. There is no traits and allocator as template parameter
//  3. size_type is uint32_t, not std::size_t
//  4. capacity() returns current size, reserve() has no effect
//  5. max_size() returns ok, but has no effect (never compares to it thus
//     never throws std::length_error)
//  6. iterator and const_iterator is plane pointer to value_type, this may be
//     different from some standard library implementations, and it is planned
//     to be changed
//  7. The meaning of npos is a little different
//  8. Use npos for insert/replace position is ok, equals to append
//
// The following is XPCOM specific:
//  1. Allows adopt/depend/literal string construction and assignment
//  2. Has clone_data and release_data operation
//
template <typename CharT>
class basic_string
{
    typedef basic_string<CharT> this_type;
public:
    typedef char_traits<CharT>                      traits_type;
    typedef CharT                                   value_type;
    typedef uint32_t                                size_type;
    typedef std::ptrdiff_t                          difference_type;
    typedef value_type*                             pointer;
    typedef const value_type*                       const_pointer;
    typedef value_type&                             reference;
    typedef const value_type&                       const_reference;

    typedef value_type*                             iterator;
    typedef const value_type*                       const_iterator;
    typedef std::reverse_iterator<iterator>         reverse_iterator;
    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;

#ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
    static const size_type npos;
#else
    static const size_type npos = static_cast<size_type>(-1);
#endif

protected:
    struct adopt_t;
    struct depend_t;
    template <size_type N> struct literal_t;

public:
    //
    // Constructors and destructor
    //
    basic_string();
    basic_string(const basic_string& s);
    basic_string(const basic_string& s, size_type pos, size_type n = npos);
    basic_string(const value_type* s, size_type n = npos);
    basic_string(size_type n, value_type c);

    template <typename InputIterator>
    basic_string(InputIterator first, InputIterator last)
    {
        typedef typename boost::is_integral<InputIterator>::type tag;
        init_aux(first, last, tag());
    }

    explicit basic_string(const adopt_t& rhs)
    {
        init_adopt(rhs.data, rhs.length);
    }

    explicit basic_string(const depend_t& rhs)
    {
        init_depend(rhs.data, rhs.length);
    }

    template <size_type N>
    explicit basic_string(const literal_t<N>& rhs)
    {
        BOOST_STATIC_ASSERT(N >= 1);
        init_depend(rhs.data, N - 1);
    }

    ~basic_string();

    // Special tag helper
    static adopt_t adopt(value_type* s, size_type n = npos)
    {
        return adopt_t(s, n);
    }

    static depend_t depend(const value_type* s, size_type n = npos)
    {
        return depend_t(s, n);
    }

    template <size_type N>
    static literal_t<N> literal(const value_type (&s)[N])
    {
        return literal_t<N>(s);
    }

    //
    // Assignment
    //
    basic_string& operator=(const basic_string& s)
    {
        return assign(s);
    }

    basic_string& operator=(const value_type* s)
    {
        return assign(s);
    }

    basic_string& operator=(value_type c)
    {
        return assign(&c, size_type(1));
    }

    basic_string& assign(const basic_string& s);
    basic_string& assign(const basic_string& s, size_type pos, size_type n);
    basic_string& assign(const value_type* s, size_type n = npos);

    basic_string& assign(size_type n, value_type c)
    {
        return replace(size_type(0), npos, n, c);
    }

    template <typename InputIterator>
    basic_string& assign(InputIterator first, InputIterator last)
    {
        return replace(begin(), end(), first, last);
    }

    basic_string& assign(const adopt_t& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    basic_string& assign(const depend_t& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    template <size_type N>
    basic_string& assign(const literal_t<N>& rhs)
    {
        this_type(rhs).swap(*this);
        return *this;
    }

    //
    // Iterators
    //
    const_iterator begin() const;
    iterator begin();
    const_iterator end() const;
    iterator end();

    const_reverse_iterator rbegin() const
    {
        return const_reverse_iterator(end());
    }

    reverse_iterator rbegin()
    {
        return reverse_iterator(end());
    }

    const_reverse_iterator rend() const
    {
        return const_reverse_iterator(begin());
    }

    reverse_iterator rend()
    {
        return reverse_iterator(begin());
    }

    //
    // Size, capacity, etc
    //
    size_type size() const;

    size_type length() const
    {
        return size();
    }

    size_type max_size() const
    {
        return (size_type(-1) / sizeof(value_type)) - 1;
    }

    void resize(size_type n)
    {
        resize(n, value_type());
    }

    void resize(size_type n, value_type c);

    size_type capacity() const
    {
        // XXX There is no API to retrieve capacity
        return size();
    }

    void reserve(size_type n = 0)
    {
        // XXX No-op, also should change operator+ when reserve is implemented
    }

    void clear();

    bool empty() const
    {
        return size() == 0;
    }

    //
    // Element access
    //
    const_reference operator[](size_type pos) const
    {
        XPCOM_ASSERT(pos <= size()); // can be at terminator
        return *(begin() + pos);
    }

    reference operator[](size_type pos)
    {
        XPCOM_ASSERT(pos < size());
        return *(begin() + pos);
    }

    const_reference at(size_type pos) const;
    reference at(size_type pos);

    //
    // Append
    //
    basic_string& operator+=(const basic_string& s)
    {
        return append(s);
    }

    basic_string& operator+=(const value_type* s)
    {
        return append(s);
    }

    basic_string& operator+=(value_type c)
    {
        push_back(c);
        return *this;
    }

    basic_string& append(const basic_string& s)
    {
        return replace(npos, size_type(0), s);
    }

    basic_string& append(const basic_string& s, size_type pos, size_type n)
    {
        return replace(npos, size_type(0), s, pos, n);
    }

    basic_string& append(const value_type* s, size_type n = npos)
    {
        return replace(npos, size_type(0), s, n);
    }

    basic_string& append(size_type n, value_type c)
    {
        return replace(npos, size_type(0), n, c);
    }

    template <typename InputIterator>
    basic_string& append(InputIterator first, InputIterator last)
    {
        iterator i = end();
        return replace(i, i, first, last);
    }

    void push_back(value_type c)
    {
        replace(npos, size_type(0), &c, size_type(1));
    }

    void pop_back()
    {
        XPCOM_ASSERT(!empty());
        erase(size() - 1, size_type(1));
    }

    //
    // Insert
    //
    basic_string& insert(size_type pos, const basic_string& s)
    {
        return replace(pos, size_type(0), s);
    }

    basic_string& insert(
            size_type pos1, const basic_string& s, size_type pos2, size_type n)
    {
        return replace(pos1, size_type(0), s, pos2, n);
    }

    basic_string& insert(size_type pos, const value_type* s, size_type n = npos)
    {
        return replace(pos, size_type(0), s, n);
    }

    basic_string& insert(size_type pos, size_type n, value_type c)
    {
        return replace(pos, size_type(0), n, c);
    }

    void insert(iterator i, size_type n, value_type c)
    {
        replace(i, i, n, c);
    }

    template <typename InputIterator>
    void insert(iterator i, InputIterator first, InputIterator last)
    {
        replace(i, i, first, last);
    }

    iterator insert(iterator i, value_type c);

    //
    // Erase
    //
    iterator erase(iterator i1, iterator i2);
    iterator erase(iterator i);

    basic_string& erase(size_type pos = 0, size_type n = npos)
    {
        return replace(pos, n, static_cast<const value_type*>(0), size_type(0));
    }

    //
    // Replace
    //
    basic_string& replace(
            size_type pos, size_type n, const basic_string& s);
    basic_string& replace(
            size_type pos1, size_type n1,
            const basic_string& s, size_type pos2, size_type n2);
    basic_string& replace(
            size_type pos, size_type n1,
            const value_type* s, size_type n2 = npos);
    basic_string& replace(
            size_type pos, size_type n1, size_type n2, value_type c);

    basic_string& replace(iterator i1, iterator i2, const basic_string& s)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());
        return replace(i1 - begin(), i2 - i1, s);
    }

    basic_string& replace(
            iterator i1, iterator i2, const value_type* s, size_type n = npos)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());
        return replace(i1 - begin(), i2 - i1, s, n);
    }

    basic_string& replace(iterator i1, iterator i2, size_type n, value_type c)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());
        return replace(i1 - begin(), i2 - i1, n, c);
    }

    basic_string& replace(
            iterator i1, iterator i2, const_iterator first, const_iterator last)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());
        XPCOM_ASSERT(first <= last);
        return replace(
                i1 - begin(), i2 - i1,
                first, static_cast<size_type>(last - first));
    }

    basic_string& replace(
            iterator i1, iterator i2, iterator first, iterator last)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());
        XPCOM_ASSERT(first <= last);
        return replace(
                i1 - begin(), i2 - i1,
                first, static_cast<size_type>(last - first));
    }

    template <typename InputIterator>
    basic_string& replace(
            iterator i1, iterator i2, InputIterator first, InputIterator last)
    {
        XPCOM_ASSERT(begin() <= i1 && i1 <= i2 && i2 <= end());

        typedef typename boost::is_integral<InputIterator>::type tag;
        return replace_dispatch(i1, i2, first, last, tag());
    }

    //
    // String operations
    //
    size_type copy(value_type* s, size_type n, size_type pos = 0) const;
    void swap(basic_string& s);

    const value_type* c_str() const
    {
        // XXX We can assume that the string is always null-terminated
        return data();
    }

    //
    // Data manipulations
    //
    // clone_data will return a character string pointer which is a clone of
    // current data.  release_data will return data/size pair of the string,
    // and reset the string to empty.
    //
    // Note1: The returned data of clone_data/release_data MUST be freed using
    //        memory::free.
    // Note2: Unlike XPCOM API, null pointer will be returned by clone_data and
    //        release_data if the string is empty.
    //
    const value_type* data() const;
    value_type* clone_data() const;
    value_type* release_data(size_type* n = 0);

    size_type find(const basic_string& s, size_type pos = 0) const;
    size_type find(const value_type* s, size_type pos = 0) const;
    size_type find(const value_type* s, size_type pos, size_type n) const;
    size_type find(value_type c, size_type pos = 0) const;

    size_type rfind(const basic_string& s, size_type pos = npos) const;
    size_type rfind(const value_type* s, size_type pos = npos) const;
    size_type rfind(const value_type* s, size_type pos, size_type n) const;
    size_type rfind(value_type c, size_type pos = npos) const;

    size_type find_first_of(
            const basic_string& s, size_type pos = 0) const;
    size_type find_first_of(
            const value_type* s, size_type pos = 0) const;
    size_type find_first_of(
            const value_type* s, size_type pos, size_type n) const;
    size_type find_first_of(
            value_type c, size_type pos = 0) const;

    size_type find_last_of(
            const basic_string& s, size_type pos = npos) const;
    size_type find_last_of(
            const value_type* s, size_type pos = npos) const;
    size_type find_last_of(
            const value_type* s, size_type pos, size_type n) const;
    size_type find_last_of(
            value_type c, size_type pos = npos) const;

    size_type find_first_not_of(
            const basic_string& s, size_type pos = 0) const;
    size_type find_first_not_of(
            const value_type* s, size_type pos = 0) const;
    size_type find_first_not_of(
            const value_type* s, size_type pos, size_type n) const;
    size_type find_first_not_of(
            value_type c, size_type pos = 0) const;

    size_type find_last_not_of(
            const basic_string& s, size_type pos = npos) const;
    size_type find_last_not_of(
            const value_type* s, size_type pos = npos) const;
    size_type find_last_not_of(
            const value_type* s, size_type pos, size_type n) const;
    size_type find_last_not_of(
            value_type c, size_type pos = npos) const;

    basic_string substr(size_type pos = 0, size_type n = npos) const;

    int compare(
            const basic_string& s) const;
    int compare(
            size_type pos1, size_type n1,
            const basic_string& s) const;
    int compare(
            size_type pos1, size_type n1,
            const basic_string& s, size_type pos2, size_type n2) const;
    int compare(
            const value_type* s) const;
    int compare(
            size_type pos1, size_type n1,
            const value_type* s) const;
    int compare(
            size_type pos1, size_type n1,
            const value_type* s, size_type n2) const;

    // Optimized version of equality comparison between two strings
    bool operator==(const basic_string<CharT>& rhs) const;

protected:
    struct adopt_t
    {
        value_type* data;
        size_type length;
        adopt_t(value_type* s, size_type n) : data(s), length(n) {}
    };

    struct depend_t
    {
        const value_type* data;
        size_type length;
        depend_t(const value_type* s, size_type n) : data(s), length(n) {}
    };

    template <size_type N>
    struct literal_t
    {
        const value_type (&data)[N];
        literal_t(const value_type (&s)[N]) : data(s) {}
    };

    struct data_type
    {
        const value_type* data;
        size_type length;
    };

    struct mutable_data_type
    {
        value_type* data;
        size_type length;
    };

    void init();
    void init(const value_type* s, size_type n, uint32_t flags = 0);
    void init(size_type n, value_type c);
    void init_adopt(const value_type* s, size_type n);
    void init_depend(const value_type* s, size_type n);
    void finish();

    value_type* mutable_data();
    void get_data(data_type& data) const;
    void get_mutable_data(mutable_data_type& data, size_type len = npos);

private:
    size_type check_pos(size_type pos) const
    {
        if (pos != npos && pos > size())
            throw_out_of_range();
        return pos;
    }

    template <typename InputIterator>
    void init_aux(InputIterator first, InputIterator last, boost::true_type)
    {
        return init(
                static_cast<size_type>(first), static_cast<value_type>(last));
    }

    template <typename InputIterator>
    void init_aux(InputIterator first, InputIterator last, boost::false_type)
    {
        typedef typename
                std::iterator_traits<InputIterator>::iterator_category tag;
        return init(first, last, tag());
    }

    template <typename InputIterator>
    void init(
            InputIterator first, InputIterator last,
            std::input_iterator_tag);

    template <typename ForwardIterator>
    void init(
            ForwardIterator first, ForwardIterator last,
            std::forward_iterator_tag);

    template <typename Iterator>
    static void copy_chars(value_type* p, Iterator first, Iterator last)
    {
        for ( ; first != last; ++first, ++p)
            traits_type::assign(*p, *first);
    }

    static void copy_chars(
            value_type* p, const_iterator first, const_iterator last)
    {
        traits_type::copy(p, first, last - first);
    }

    static void copy_chars(value_type* p, iterator first, iterator last)
    {
        traits_type::copy(p, first, last - first);
    }

    template <typename InputIterator>
    basic_string& replace_dispatch(
            iterator i1, iterator i2,
            InputIterator first, InputIterator last, boost::true_type)
    {
        return replace(
                i1 - begin(), i2 - i1,
                static_cast<size_type>(first), static_cast<value_type>(last));
    }

    template <typename InputIterator>
    basic_string& replace_dispatch(
            iterator i1, iterator i2,
            InputIterator first, InputIterator last, boost::false_type)
    {
        const basic_string s(first, last);
        return replace(i1 - begin(), i2 - i1, s);
    }

    // Binary representation of string container
    struct container
    {
#if XPCOM_API_VERSION < XPCOM_API_VERSION_1_9
        void* v; // not available from version 1.9
#endif
        void* d1;
        uint32_t d2;
#if XPCOM_API_VERSION >= XPCOM_API_VERSION_1_9_0_3
        uint32_t d3; // XXX in fact, this is changed to PRUint32 from 1.9.0.3
#else
        void* d3;
#endif
    } data_;
};

#ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
template <typename CharT>
const typename basic_string<CharT>::size_type
basic_string<CharT>::npos =
    static_cast<typename basic_string<CharT>::size_type>(-1);
#else
template <typename CharT>
const typename basic_string<CharT>::size_type
basic_string<CharT>::npos;
#endif

template <typename CharT>
template <typename InputIterator>
void basic_string<CharT>::init(
        InputIterator first, InputIterator last, std::input_iterator_tag)
{
    init();
    if (first == last)
        return;

    value_type buf[128];
    size_type n = 0;

    try {
        while (true) {
            size_type len = 0;
            while (first != last && len < (sizeof(buf) / sizeof(buf[0]))) {
                buf[len++] = *first;
                ++first;
            }

            mutable_data_type d;
            get_mutable_data(d, n + len);
            if (!d.data)
                throw_bad_alloc();

            traits_type::copy(d.data + n, buf, len);

            if (first == last)
                break;

            n += len;
        }

    } catch (...) {
        finish();
        throw;
    }
}

template <typename CharT>
template <typename ForwardIterator>
void basic_string<CharT>::init(
        ForwardIterator first, ForwardIterator last, std::forward_iterator_tag)
{
    init();
    if (first == last)
        return;

    const size_type n = static_cast<size_type>(std::distance(first, last));

    mutable_data_type d;
    get_mutable_data(d, n);
    if (!d.data) {
        finish();
        throw_bad_alloc();
    }

    try {
        copy_chars(d.data, first, last);
    } catch (...) {
        finish();
        throw;
    }
}

// operator +
template <typename CharT>
inline basic_string<CharT>
operator+(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    basic_string<CharT> str(lhs);
    str.append(rhs);
    return str;
}

template <typename CharT>
inline basic_string<CharT>
operator+(const basic_string<CharT>& lhs, const CharT* rhs)
{
    //XPCOM_ASSERT(rhs);
    basic_string<CharT> str(lhs);
    str.append(rhs);
    return str;
}

template <typename CharT>
inline basic_string<CharT>
operator+(const CharT* lhs, const basic_string<CharT>& rhs)
{
    //XPCOM_ASSERT(lhs);
    basic_string<CharT> str(lhs);
    str.append(rhs);
    return str;
}

template <typename CharT>
inline basic_string<CharT>
operator+(const basic_string<CharT>& lhs, CharT rhs)
{
    typedef typename basic_string<CharT>::size_type size_type;

    basic_string<CharT> str(lhs);
    str.append(size_type(1), rhs);
    return str;
}

template <typename CharT>
inline basic_string<CharT>
operator+(CharT lhs, const basic_string<CharT>& rhs)
{
    typedef typename basic_string<CharT>::size_type size_type;

    basic_string<CharT> str(size_type(1), lhs);
    str.append(rhs);
    return str;
}

// Comparisons
//template <typename CharT>
//inline bool
//operator==(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
//{
//    return lhs.compare(rhs) == 0;
//}

template <typename CharT>
inline bool
operator==(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return lhs.compare(rhs) == 0;
}

template <typename CharT>
inline bool
operator==(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return rhs.compare(lhs) == 0;
}

template <typename CharT>
inline bool
operator!=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    return !(lhs == rhs);
}

template <typename CharT>
inline bool
operator!=(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return !(lhs == rhs);
}

template <typename CharT>
inline bool
operator!=(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return !(lhs == rhs);
}

template <typename CharT>
inline bool
operator<(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    return lhs.compare(rhs) < 0;
}

template <typename CharT>
inline bool
operator<(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return lhs.compare(rhs) < 0;
}

template <typename CharT>
inline bool
operator<(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return rhs.compare(lhs) > 0;
}

template <typename CharT>
inline bool
operator>(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    return lhs.compare(rhs) > 0;
}

template <typename CharT>
inline bool
operator>(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return lhs.compare(rhs) > 0;
}

template <typename CharT>
inline bool
operator>(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return rhs.compare(lhs) < 0;
}

template <typename CharT>
inline bool
operator<=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    return lhs.compare(rhs) <= 0;
}

template <typename CharT>
inline bool
operator<=(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return lhs.compare(rhs) <= 0;
}

template <typename CharT>
inline bool
operator<=(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return rhs.compare(lhs) >= 0;
}

template <typename CharT>
inline bool
operator>=(const basic_string<CharT>& lhs, const basic_string<CharT>& rhs)
{
    return lhs.compare(rhs) >= 0;
}

template <typename CharT>
inline bool
operator>=(const basic_string<CharT>& lhs, const CharT* rhs)
{
    return lhs.compare(rhs) >= 0;
}

template <typename CharT>
inline bool
operator>=(const CharT* lhs, const basic_string<CharT>& rhs)
{
    return rhs.compare(lhs) <= 0;
}

// swap
template <typename CharT>
inline void
swap(basic_string<CharT>& lhs, basic_string<CharT>& rhs)
{
    lhs.swap(rhs);
}

// I/O
#ifndef XPCOM_NO_IOSTREAM

template <typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& operator>>(
        std::basic_istream<CharT, Traits>& is, basic_string<CharT>& rhs)
{
    std::basic_string<CharT, Traits> s;
    if (is >> s) {
        rhs.assign(
                s.data(),
                static_cast<typename basic_string<CharT>::size_type>(s.size()));
    }
    return is;
}

template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(
        std::basic_ostream<CharT, Traits>& os, const basic_string<CharT>& rhs)
{
    std::basic_string<CharT, Traits> s(rhs.data(), rhs.size());
    os << s;
    return os;
}

template <typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& getline(
        std::basic_istream<CharT, Traits>& is,
        basic_string<CharT>& str, CharT delim)
{
    std::basic_string<CharT, Traits> s;
    if (std::getline(is, s, delim)) {
        str.assign(
                s.data(),
                static_cast<typename basic_string<CharT>::size_type>(s.size()));
    }
    return is;
}

template <typename CharT, typename Traits>
inline
std::basic_istream<CharT, Traits>& getline(
        std::basic_istream<CharT, Traits>& is, basic_string<CharT>& str)
{
    return getline(is, str, is.widen('\n'));
}

#endif // XPCOM_NO_IOSTREAM

// Explicit instantiation declaration
#ifdef XPCOM_HAS_EXTERN_TEMPLATE
XPCOM_EXTERN_TEMPLATE class XPCOM_TEMPLATE_DECL basic_string<char>;
XPCOM_EXTERN_TEMPLATE class XPCOM_TEMPLATE_DECL basic_string<wchar>;
#if defined(BOOST_MSVC) && BOOST_MSVC >= 1400
XPCOM_EXTERN_TEMPLATE XPCOM_TEMPLATE_DECL
basic_string<char>::basic_string(char*, char*);
XPCOM_EXTERN_TEMPLATE XPCOM_TEMPLATE_DECL
basic_string<char>::basic_string(const char*, const char*);
XPCOM_EXTERN_TEMPLATE XPCOM_TEMPLATE_DECL
basic_string<wchar>::basic_string(wchar*, wchar*);
XPCOM_EXTERN_TEMPLATE XPCOM_TEMPLATE_DECL
basic_string<wchar>::basic_string(const wchar*, const wchar*);
#endif
#endif // XPCOM_HAS_EXTERN_TEMPLATE

} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_STRING_HPP
